Skip to content

Conversation

ghost
Copy link

@ghost ghost commented Feb 23, 2021

@ghost ghost added feature Adds functionality to the library c: export Component: export (mod export, derive) labels Feb 23, 2021
@ghost
Copy link
Author

ghost commented Mar 6, 2021

bors r+

bors bot added a commit that referenced this pull request Mar 6, 2021
701: Add automatically_derived and allow lints in macros r=toasteater a=toasteater

This should prevent clippy lints from showing up in user code in the future. See:

- https://doc.rust-lang.org/reference/attributes/derive.html
- https://doc.rust-lang.org/rustc/lints/groups.html
- https://github.com/rust-lang/rust-clippy#clippy

Co-authored-by: toasteater <[email protected]>
@bors
Copy link
Contributor

bors bot commented Mar 6, 2021

Build failed:

This should prevent clippy lints from showing up in user code in the future.
@ghost
Copy link
Author

ghost commented Apr 15, 2021

Also finally fixing this 😅

bors retry

bors bot added a commit that referenced this pull request Apr 15, 2021
701: Add automatically_derived and allow lints in macros r=toasteater a=toasteater

This should prevent clippy lints from showing up in user code in the future. See:

- https://doc.rust-lang.org/reference/attributes/derive.html
- https://doc.rust-lang.org/rustc/lints/groups.html
- https://github.com/rust-lang/rust-clippy#clippy

709: Minimal async-await foundations r=toasteater a=toasteater

This sets the foundations for async-await support in godot-rust, based on the original idea in #284. However, although the tests work, this is not a full implementation:

- Async methods can only be registered manually using `build_method`. Macro syntax and implementation are out of the scope of this PR.
- The runtime types aren't registered automatically yet. Users need to manually call `register_runtime` and `terminate_runtime` functions in their library lifecycle hooks. Improving this is out of the scope of this PR for now.
- The crate is currently re-exported as `gdnative::asn`, instead of the much longer `async_yield`. The name is open to discussion -- I don't like it very much.
- Only local spawners are supported, due to issues with thread safety. Users may off-load tasks that don't contain `yield`-likes to thread pool spawners using something like `futures::future::Remote`, however.
- Panics in async methods don't currently behave very well. Their `FunctionState`-likes simply block forever and any outstanding bridge objects for futures can be leaked.

- - -

While the feature is not yet complete, the commit is already pretty big, and I feel that it's in a somewhat usable state. As a result, I'm putting this up as a draft PR to gather some feedback. If you have uses for async-await / "yield" from GDScript, please feel free to try it and tell me what you think!

Registering an async method currently looks like this (excerpt from the tests):

```rust

struct ResumeAddFn;

impl AsyncMethod<AsyncMethods> for ResumeAddFn {
    fn spawn_with(&self, spawner: Spawner<'_, AsyncMethods>) {
        spawner.spawn(|ctx, _this, mut args| {
            let a = args.read::<i32>().get().unwrap();
            let obj = args.read::<Ref<Object>>().get().unwrap();
            let name = args.read::<GodotString>().get().unwrap();

            async move {
                let b = ctx.until_resume().await;
                let b = i32::from_variant(&b).unwrap();

                let c = unsafe { obj.assume_safe().call(name, &[]) };
                let c = Ref::<Reference>::from_variant(&c).unwrap();
                let c = unsafe { c.assume_safe() };
                let c = ctx.signal(c, "completed").unwrap().await;
                assert_eq!(1, c.len());
                let c = i32::from_variant(&c[0]).unwrap();

                (a + b + c).to_variant()
            }
        });
    }
}

fn register_methods(builder: &ClassBuilder<AsyncMethods>) {
    builder
        .build_method("resume_add", Async::new(ResumeAddFn))
        .done();
}
```

Using it is almost like any other GDScript coroutine:

```gdscript
func _async_call(obj):
	var fn_state = obj.resume_add(1, self, "_get_async_number")
	# the "resumable" signal is unique to Rust, since unlike GDScript coroutines,
	# Rust futures aren't guaranteed to be polled up to a yield right after spawning.
	yield(fn_state, "resumable")
	fn_state = fn_state.resume(2)
	# no "resumable" signal when awaiting signals, though
	var result = yield(fn_state, "completed")
	assert(result == 42)

func _get_async_number():
	yield(get_tree().create_timer(0.1), "timeout")
	return 39
```

Co-authored-by: toasteater <[email protected]>
@bors
Copy link
Contributor

bors bot commented Apr 15, 2021

Build failed (retrying...):

@bors
Copy link
Contributor

bors bot commented Apr 15, 2021

Build succeeded:

@bors bors bot merged commit 37a35c5 into godot-rust:master Apr 15, 2021
@ghost ghost mentioned this pull request May 1, 2021
7 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c: export Component: export (mod export, derive) feature Adds functionality to the library

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants